package org.streets.commons.util;

/**
 * Utility for scan hibernate xml config in jar file.  
 * 
 * @author dzb
 */
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class JarXmlUtils {
	
	private static final Log log = LogFactory.getLog(JarXmlUtils.class.getName());
		
	private static boolean inTypes(String type, String... fileTypes) {
		for (String s : fileTypes) {
			if (type.endsWith(s)) return true;
		}
		return false;
	}
	/**
	 * Read all mappings from a jar file
	 * <p/>
	 * Assumes that any file named <tt>*.hbm.xml</tt> is a mapping document.
	 *
	 * @param jar a jar file
	 * @return this (for method chaining purposes)
	 * @throws Exception Indicates problems reading the jar file or
	 * processing the contained mapping documents.
	 */
	public static void scanForFiles(JarFile jarFile, String path, List<InputStream> files, String... fileTypes) {

		Enumeration<JarEntry> jarEntries = jarFile.entries();
		while ( jarEntries.hasMoreElements() ) {
			JarEntry ze = jarEntries.nextElement();
			//ZipEntry ze = (ZipEntry) jarEntries.nextElement();			
			if (ze.isDirectory()) continue;
			if (ze.getName().startsWith(path) && inTypes(ze.getName(), fileTypes)) {
				log.info("--> Found mapping document in jar: " + ze.getName());
				try {
					files.add(jarFile.getInputStream(ze));
				}
				catch (Exception e) {
					log.error("Could not read mapping documents from jar: " + jarFile.getName()+". Error: "+e.getMessage(), e);
				}
			}
		}
	}
	
	public static void scanForEntityFiles(JarFile jarFile, String path, List<String> files, String... fileTypes ) {

		Enumeration<JarEntry> jarEntries = jarFile.entries();
		while ( jarEntries.hasMoreElements() ) {
			JarEntry ze = jarEntries.nextElement();			
			if (ze.isDirectory()) continue;
			if (ze.getName().startsWith(path) &&  ze.getName().endsWith(".class") ) {
				log.info("--> Found mapping document in jar: " + ze.getName());
				try {
					InputStream stream = jarFile.getInputStream(ze);
					DataInputStream dstream = new DataInputStream( new BufferedInputStream(stream) );			
					ClassFile cf = null;					
					try {
						try {
							cf = new ClassFile( dstream );
						}
						finally {
							dstream.close();
							stream.close();
						}
					} catch (IOException e) {
						throw new RuntimeException( e );
					}

					AnnotationsAttribute visible = (AnnotationsAttribute) cf.getAttribute( AnnotationsAttribute.visibleTag );
					if ( visible != null ) {
						boolean isEntity = visible.getAnnotation("javax.persistence.Entity") != null;
						if ( isEntity ) {
							log.info( "found EJB3 Entity bean: " + cf.getName() );
							files.add( cf.getName() );
						}
						boolean isEmbeddable = visible.getAnnotation("javax.persistence.Embeddable") != null;
						if ( isEmbeddable ) {
							log.info( "found EJB3 @Embeddable: " + cf.getName() );
							files.add( cf.getName() );
						}
						boolean isEmbeddableSuperclass = visible.getAnnotation("javax.persistence.MappedSuperclass") != null;
						if ( isEmbeddableSuperclass ) {
							log.info( "found EJB3 @MappedSuperclass: " + cf.getName() );
							files.add( cf.getName() );
						}
					}
				} catch (Exception e) {
					log.error("Could not read mapping documents from jar: " + jarFile.getName()+". Error: "+e.getMessage(), e);
				}
			}
		}
	}
	
	/**
	 * 
	 * @param file
	 * @param mode 
	 * @return
	 */
	public static String classFile2ClassName(File file, boolean validJPAEntity) {
		
		FileInputStream stream = null;
		
		try {
			stream = new FileInputStream(file);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
		
		if (stream == null) return null;
		
		DataInputStream dstream = new DataInputStream( new BufferedInputStream(stream));			
		
		ClassFile cf = null;
		
		try {
			try {
				cf = new ClassFile( dstream );
			}
			finally {
				dstream.close();
				stream.close();
			}
		} catch (IOException e) {
			throw new RuntimeException( e );
		}
		
		
		if (validJPAEntity) {
			
			AnnotationsAttribute visible = (AnnotationsAttribute) cf.getAttribute( AnnotationsAttribute.visibleTag );
			
			if (visible == null) return null;
			
			boolean isEntity = visible.getAnnotation("javax.persistence.Entity") != null;
			if ( isEntity ) {
				log.info( "found EJB3 Entity bean: " + cf.getName() );
				return cf.getName();
			}
			boolean isEmbeddable = visible.getAnnotation("javax.persistence.Embeddable") != null;
			if ( isEmbeddable ) {
				log.info( "found EJB3 @Embeddable: " + cf.getName() );
				return cf.getName();
			}
			boolean isEmbeddableSuperclass = visible.getAnnotation("javax.persistence.MappedSuperclass") != null;
			if ( isEmbeddableSuperclass ) {
				log.info( "found EJB3 @MappedSuperclass: " + cf.getName() );
				return cf.getName();
			}
			
			return null;
		}

		return cf.getName();
	}
	
}
